#include <windows.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

// 定义游戏区域的大小
const int WIDTH = 40;
const int HEIGHT = 20;
const int SNAKELEN = 5;

// 定义蛇身结构体
struct SnakePart {
    int x;
    int y;
};

// 用于存储整个游戏画面内容的缓冲区（二维字符数组）
char buffer[HEIGHT][WIDTH];

// 初始化缓冲区，填充空格字符
void initBuffer() {
    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDTH; j++) {
            buffer[i][j] = (char)(' ');
        }
    }
}

// 绘制游戏边界到缓冲区
void drawBorderToBuffer() {
    // 绘制上边框
    for (int i = 0; i < WIDTH; i++) {
        buffer[0][i] = '#';
    }
    // 绘制下边框
    for (int i = 0; i < WIDTH; i++) {
        buffer[HEIGHT - 1][i] = '#';
    }
    // 绘制左右边框
    for (int j = 1; j < HEIGHT - 1; j++) {
        buffer[j][0] = '#';
        buffer[j][WIDTH - 1] = '#';
    }
}

// 绘制蛇身到缓冲区
void drawSnakeToBuffer(SnakePart *snake, int snakeSize) {
    for (int i = 0; i < snakeSize; i++) {
        if (snake[i].x >= 0 && snake[i].x < WIDTH && snake[i].y >= 0 && snake[i].y < HEIGHT) {
            buffer[snake[i].y][snake[i].x] = (char)('O');
        }
    }
}

// 绘制豆子到缓冲区
void drawFoodToBuffer(int foodX, int foodY) {
    if (foodX >= 0 && foodX < WIDTH && foodY >= 0 && foodY < HEIGHT) {
        buffer[foodY][foodX] = (char)('*');
    }
}

// 将缓冲区内容输出到控制台
void printBuffer() {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coord = { 0, 0 };
    SetConsoleCursorPosition(hConsole, coord);
    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDTH; j++) {
            putchar(buffer[i][j]);
        }
        putchar('\n');
    }
}

// 移动蛇
void moveSnake(SnakePart **snake, int *snakeSize, int dx, int dy, bool ateFood) {
    // 先创建一个临时数组，用于存储移动后的蛇身信息
    SnakePart *tempSnake = (SnakePart *)malloc((*snakeSize + (ateFood? 1 : 0)) * sizeof(SnakePart));
    if (tempSnake == NULL) {
        printf("内存分配失败！\n");
        exit(1);
    }

    // 移动蛇身，将每个部分移到前一个部分的位置，除了头部
    for (int i = (*snakeSize) - 1; i > 0; i--) {
        tempSnake[i].x = (*snake)[i - 1].x;
        tempSnake[i].y = (*snake)[i - 1].y;
    }
    // 移动头部
    tempSnake[0].x = (*snake)[0].x + dx;
    tempSnake[0].y = (*snake)[0].y + dy;

    // 如果吃到食物，添加新的蛇身部分（复制蛇尾坐标）
    if (ateFood) {
        tempSnake[*snakeSize].x = (*snake)[*snakeSize - 1].x;
        tempSnake[*snakeSize].y = (*snake)[*snakeSize - 1].y;
    }

    // 释放原来蛇身数组占用的内存
    free(*snake);

    // 将临时数组赋值给原来的蛇身数组指针，并更新蛇身长度（只有吃到食物时才增加长度）
    *snake = tempSnake;
    if (ateFood) {
        (*snakeSize)++;
    }
}

// 检查蛇是否撞墙
bool checkCollisionWithWall(SnakePart *snake, int snakeSize) {
    if (snake[0].x < 1 || snake[0].x >= WIDTH - 1 || snake[0].y < 1 || snake[0].y >= HEIGHT - 1) {
        return true;
    }
    return false;
}

// 检查蛇是否撞到自己
bool checkCollisionWithSelf(SnakePart *snake, int snakeSize) {
    for (int i = 1; i < snakeSize; i++) {
        if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
            return true;
        }
    }
    return false;
}

// 检查蛇是否吃到食物
bool checkEatFood(SnakePart *snake, int snakeSize, int foodX, int foodY) {
    if (snake[0].x == foodX && snake[0].y == foodY) {
        return true;
    }
    return false;
}

// 随机生成食物位置
void generateFood(int *foodX, int *foodY) {
    *foodX = (rand() % (WIDTH - 2)) + 1;
    *foodY = (rand() % (HEIGHT - 2)) + 1;
}

int main() {
    // 设置随机数种子，只在程序开始时调用一次
    srand((unsigned int)time(NULL));

    // 隐藏控制台光标
    CONSOLE_CURSOR_INFO cursorInfo;
    cursorInfo.dwSize = 100;
    cursorInfo.bVisible = false;
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursorInfo);

    // 初始化蛇身，初始长度为SNAKELEN，从屏幕中间开始
    SnakePart *snake = (SnakePart *)malloc(SNAKELEN * sizeof(SnakePart));
    if (snake == NULL) {
        printf("内存分配失败！\n");
        exit(1);
    }
    snake[0].x = WIDTH / 2;
    snake[0].y = HEIGHT / 2;
    for (int i = 1; i < SNAKELEN; i++) {
        snake[i].x = snake[0].x - i;
        snake[i].y = snake[0].y;
    }
    int snakeSize = SNAKELEN;

    int dx = 1;  // 初始水平方向移动量
    int dy = 0;  // 初始垂直方向移动量

    bool gameOver = false;

    int foodX, foodY;
    generateFood(&foodX, &foodY);

    initBuffer();
    drawBorderToBuffer();
    drawSnakeToBuffer(snake, snakeSize);
    drawFoodToBuffer(foodX, foodY);
    printBuffer();

    while (!gameOver) {
        if (_kbhit()) {  // 检查是否有键盘输入
            char key = _getch();
            switch (key) {
            case 'w':
                if (dy!= 1) {
                    dy = -1;
                    dx = 0;
                }
                break;
            case 's':
                if (dy!= -1) {
                    dy = 1;
                    dx = 0;
                }
                break;
            case 'a':
                if (dx!= 1) {
                    dy = 0;
                    dx = -1;
                }
                break;
            case 'd':
                if (dx!= -1) {
                    dy = 0;
                    dx = 1;
                }
                break;
            }
        }

        bool ateFood = checkEatFood(snake, snakeSize, foodX, foodY);
        moveSnake(&snake, &snakeSize, dx, dy, ateFood);

        if (checkCollisionWithWall(snake, snakeSize) || checkCollisionWithSelf(snake, snakeSize)) {
            gameOver = true;
        }

        if (ateFood) {
            // 重新生成食物位置
            generateFood(&foodX, &foodY);
        }

        // 只有当蛇的状态发生改变（移动或者吃到食物）时才更新画面
        if (dx!= 0 || dy!= 0 || ateFood) {
            initBuffer();
            drawBorderToBuffer();
            drawSnakeToBuffer(snake, snakeSize);
            drawFoodToBuffer(foodX, foodY);
            printBuffer();
        }

        Sleep(200);  // 控制蛇的移动速度，单位是毫秒
    }

    printf("游戏结束！\n");
    free(snake);
    return 0;
}